﻿using UnityEngine;

namespace Gp.Scripts.Character {
    public class FovSight: MonoBehaviour, IFov, ISightAware {
        [SerializeField]
        private float fieldOfViewAngle = 90f;
        [SerializeField]
        private float aspect = 1.77f;
        [SerializeField]
        private float farDistance = 100f;

        public float FieldOfViewAngle => fieldOfViewAngle;
        public float Aspect => aspect;
        public float FarDistance => farDistance;
        public float HorizontalAngle => FieldOfViewAngle * Aspect;
        public float HalfVerticalAngle => FieldOfViewAngle / 2.0f;
        public float HalfHorizontalAngle => HorizontalAngle / 2.0f;
        public bool IsSight(Bounds bounds) {
            var (topLeftPos, topRightPos, bottomLeftPos, bottomRightPos) = GetFovPoints();
            Vector3 originPos = transform.position;
            FovPlane[] fovPlanes = {
                new FovPlane(topLeftPos, topRightPos, bottomLeftPos),
                new FovPlane(originPos, topRightPos, topLeftPos),
                new FovPlane(originPos, topLeftPos, bottomLeftPos),
                new FovPlane(originPos, bottomLeftPos, bottomRightPos),
                new FovPlane(originPos, bottomRightPos, topRightPos)
            };
            foreach (FovPlane fovPlane in fovPlanes)
            {
                // the closet point to plane
                Vector3 p = new Vector3(
                    fovPlane.Normal.x >= 0 ? bounds.max.x : bounds.min.x,
                    fovPlane.Normal.y >= 0 ? bounds.max.y : bounds.min.y,
                    fovPlane.Normal.z >= 0 ? bounds.max.z : bounds.min.z
                );
                if (!fovPlane.OnNormalSide(p))
                    return false;
            }
            return true;
        } 
        private (Vector3 topLeft, Vector3 topRight, Vector3 bottomLeft, Vector3 bottomRight) GetFovPoints() {
            Vector3 forward = transform.forward;
            Vector3 right = transform.right;
            Vector3 up = transform.up;

            float halfVerticalFov = Mathf.Tan(FieldOfViewAngle * 0.5f * Mathf.Deg2Rad) * FarDistance;
            float halfHorizontalFov = halfVerticalFov * Aspect;

            Vector3 topLeft = transform.position + forward * FarDistance + up * halfVerticalFov - right * halfHorizontalFov;
            Vector3 topRight = transform.position + forward * FarDistance + up * halfVerticalFov + right * halfHorizontalFov;
            Vector3 bottomLeft = transform.position + forward * FarDistance - up * halfVerticalFov - right * halfHorizontalFov;
            Vector3 bottomRight = transform.position + forward * FarDistance - up * halfVerticalFov + right * halfHorizontalFov;

            return (topLeft, topRight, bottomLeft, bottomRight);
        }
    }
}